home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
v10n19.arc
/
TALK.ASM
< prev
next >
Wrap
Assembly Source File
|
1991-10-24
|
21KB
|
549 lines
title TALK --- Simple terminal emulator
page 55,132
.lfcond ;list false conditionals too
; TALK.ASM --- Simple terminal emulator program
; that demonstrates use of the DOS 5 Task Switcher API.
; Copyright (c) 1991 Ray Duncan
;
; Set the equates COM1 and ECHO to TRUE or FALSE as appropriate
; for your machine. This program does not set baud rates or
; other communications parameters; use the MODE command instead.
; Exit from TALK.EXE by entering Alt-X.
stdout equ 1 ; standard output handle
cr equ 0dh ; ASCII carriage return
lf equ 0ah ; ASCII line feed
AltX equ 02dh ; keycode for Alt-X
bufsiz equ 4096 ; size of serial port buffer
true equ -1
false equ 0
com1 equ false ; Use COM1 if nonzero
com2 equ not com1 ; Use COM2 if nonzero
echo equ false ; FALSE=full-duplex,
; TRUE=half-duplex
pic_mask equ 21h ; 8259 int. mask port
pic_eoi equ 20h ; 8259 EOI port
if com1
com_data equ 03f8h ; port assignments for COM1
com_ier equ 03f9h
com_mcr equ 03fch
com_sts equ 03fdh
com_int equ 0ch ; COM1 interrupt number
int_mask equ 10h ; IRQ4 mask for 8259
endif
if com2
com_data equ 02f8h ; port assignments for COM2
com_ier equ 02f9h
com_mcr equ 02fch
com_sts equ 02fdh
com_int equ 0bh ; COM2 interrupt number
int_mask equ 08h ; IRQ3 mask for 8259
endif
_DATA segment word public 'DATA'
in_char db 0 ; PC keyboard input char.
in_flag db 0 ; <>0 if char waiting
msg1 db cr,lf,'Terminal emulator running...',cr,lf
msg1_len equ $-msg1
msg2 db cr,lf,'Exit from terminal emulator.',cr,lf
msg2_len equ $-msg2
oldcomm dd 0 ; original contents of serial
; port interrupt vector
switcher dd 0 ; address of task switcher's
; service entry point
swvers dd 0 ; pointer to SWVERSION structure
; returned by task switcher
cbinfo label word ; switcher callback info structure
cbnext dd 0 ; addr of next structure in chain
cbentry dd callback ; entry point for notify function
dd 0 ; reserved
cbapi dd apiinfo ; addr of list of api info strucs
apiinfo dw 0 ; empty list of api info structures
hooked dw 0 ; nonzero if we are hooked into
; switcher notification chain
asc_in dw 0 ; input pointer to ring buffer
asc_out dw 0 ; output pointer to ring buffer
asc_buf db bufsiz dup (?) ; communications buffer
_DATA ends
_TEXT segment word public 'CODE'
assume cs:_TEXT,ds:_DATA,ss:_STK
talk proc far
mov ax,_DATA ; make our data addressable
mov ds,ax
mov es,ax
call hook ; try and hook switcher
jc talk1 ; jump, function failed
mov hooked,-1 ; set flag that we are chained
talk1: call asc_enb ; enable communications port
mov dx,offset msg1 ; display message
mov cx,msg1_len ; 'Terminal emulator running'
mov bx,stdout ; BX = standard output handle
mov ah,40h ; Fxn 40H = write file or device
int 21h ; transfer to MS-DOS
talk2: call pc_stat ; keyboard character waiting?
jz talk4 ; nothing waiting, jump
call pc_in ; read keyboard character
cmp al,0 ; is it a function key?
jne talk3 ; not function key, jump
call pc_in ; function key, get 2nd part
cmp al,AltX ; was it Alt-X?
je talk5 ; yes, terminate program
push ax ; no, send to comm port
xor al,al ; send lead byte (0)
call com_out
pop ax ; send function key code
call com_out
jmp talk2 ; get another key
talk3: if echo ; keyboard character received
push ax ; if half-duplex, echo
call pc_out ; character to PC display
pop ax
endif
call com_out ; send char to serial port
talk4: call com_stat ; serial port input waiting?
jz talk2 ; nothing waiting, jump
call com_in ; read char from serial port
call pc_out ; send it to PC display
jmp talk4 ; see if any more waiting
talk5: call asc_dsb ; release comm hardware
cmp hooked,0 ; did we hook task switcher?
je talk6 ; no, jump
call unhook ; release switcher hooks
talk6: mov dx,offset msg2 ; display farewell message
mov cx,msg2_len ; DX=addr, CX=length
mov bx,stdout ; BX = standard output handle
mov ah,40h ; Fxn 40H = write device
int 21h ; transfer to MS-DOS
mov ax,4c00h ; terminate program with
int 21h ; return code = 0
talk endp
; The variable OLDMUX holds the previous contents of the Int 2FH
; vector. The variable is in the code segment so that it is
; addressable at interrupt time without changing DS.
oldmux dd 0 ; prev owner of Int 2FH
;
; HOOK - Hook switcher notification chain and multiplex interrupt.
;
; Call with: Nothing
; Returns: Carry = clear if switcher was hooked
; Carry = set if switcher not present or could
; not be hooked.
; Destroys: AX, BX, CX, DX, DI, ES
;
hook proc near
xor bx,bx ; check if switcher present
mov di,bx ; BX, ES:DI must be zero
mov es,bx
mov ax,4b02h ; multiplex function 4b02h
int 2fh ; is detect switcher call
mov ax,es ; check for ES:DI=0
or ax,di
jz hook2 ; jump if no switcher
mov word ptr switcher,di ; save address of switcher
mov word ptr switcher+2,es ; service entry point
xor ax,ax ; get pointer to switcher's
call [switcher] ; version data structure
jc hook2 ; jump, function failed
mov word ptr swvers,bx ; otherwise pointer was
mov word ptr swvers+2,es ; returned in ES:BX
cmp word ptr es:[bx],1 ; check switcher protocol version
jne hook2 ; we insist on version 1.0 or we
cmp word ptr es:[bx+2],0 ; don't hook notification chain
jne hook2 ; jump, not 1.0
mov ax,4 ; now hook notification chain
mov di,ds ; switcher function = 4
mov es,di ; ES:DI = addr of switcher
mov di,offset _DATA:cbinfo ; callback info structure
call [switcher] ; call switcher entry point
jc hook2 ; jump, function failed
mov ax,352fh ; get current vector for
int 21h ; multiplex interrupt 2FH
mov word ptr oldmux+2,es ; and save it
mov word ptr oldmux,bx
push ds ; save our data segment
mov ax,cs ; set DS:DX = address
mov ds,ax ; of our multiplex handler
mov dx,offset muxisr ; to hook Int 2FH
mov ax,252fh ; Fxn 25H = set vector
int 21h ; transfer to MS-DOS
pop ds ; restore data segment
clc ; return carry = clear
ret ; if switcher was hooked
hook2: stc ; return carry = set
ret ; if switcher not hooked
hook endp
;
; UNHOOK - Release switcher notification chain and multiplex interrupt.
;
; Call with: Nothing
; Returns: Nothing
; Destroys: AX, DX, DI, ES
;
unhook proc near
push ds ; save our data segment
lds dx,oldmux ; load address of previous
; multiplex interrupt owner
mov ax,252fh ; Fxn 25H = set vector
int 21h ; transfer to MS-DOS
pop ds ; restore data segment
mov ax,5 ; unhook from notify chain
mov di,ds ; switcher function = 5
mov es,di ; ES:DI = address of
mov di,offset _DATA:cbinfo ; callback info structure
call [switcher] ; call switcher entry point
ret
unhook endp
;
; MUXISR - Handler for multiplex interrupt.
;
; Call with: AH = multiplex number, other registers vary.
; Returns: ES:BX = address of callback info structure
; if multiplex function 4BH subfunction 01H detected,
; otherwise changes nothing, chains to previous owner.
; Destroys: Nothing
;
muxisr proc far
cmp ax,4b01h ; is this Build Notify Chain?
je mux2 ; yes, process it
jmp [oldmux] ; no, chain to old handler
mux2: push ds ; save updated switcher
push ax ; entry point address
mov ax,_DATA
mov ds,ax
mov word ptr switcher+2,cx
mov word ptr switcher,dx
pop ax
pop ds
pushf ; call down through the
call [oldmux] ; multiplex handler chain
push ds ; now save address of next
push ax ; guy's callback info structure
mov ax,_DATA ; in our callback info structure
mov ds,ax
mov word ptr cbnext,bx
mov word ptr cbnext+2,es
mov es,ax ; let ES:BX = address of
mov bx,offset _DATA:cbinfo ; our callback info structure
pop ax ; and pass it back to switcher
pop ds
iret ; return from multiplex interrupt
muxisr endp
;
; CALLBACK - Switcher callback function installed by HOOK and MUXISR.
; Looks for session switch notifications and refuses same.
;
; Call with: AX = notification type, other registers vary.
; Returns: AX <> 0 if Query Suspend Session or Suspend Session
; notifications, otherwise AX = 0.
; Destroys: Nothing
;
callback proc far
cmp ax,1 ; is it Query Suspend callback?
je callb2 ; yes, return 1 to block suspend
cmp ax,2 ; is it Suspend Session callback?
je callb2 ; yes, return 1 to block suspend
xor ax,ax ; else return success
callb2: retf
callback endp
;
; COM_STAT - Check communications port status
;
; Call with: Nothing
; Returns: Zero flag = false if character ready
; Zero flag = true if no character waiting
; Destroys: Nothing
;
com_stat proc near
push ax ; save AX
mov ax,asc_in ; compare ring buffer pointers
cmp ax,asc_out ; to set Zero flag
pop ax ; restore AX
ret ; return to caller
com_stat endp
;
; COM_IN - Get character from serial port
;
; Call with: Nothing
; Returns: AL = character
; Destroys: Nothing
;
com_in proc near
push bx ; save register BX
com_in1: ; if no char waiting, wait
mov bx,asc_out ; until one is received
cmp bx,asc_in
je com_in1 ; jump, nothing waiting
mov al,[bx+asc_buf] ; extract char from buffer
inc bx ; update buffer pointer
cmp bx,bufsiz ; time to wrap pointer?
jne com_in2 ; no, jump
xor bx,bx ; yes, reset pointer
com_in2:
mov asc_out,bx ; store updated pointer
pop bx ; restore register BX
ret ; and return to caller
com_in endp
;
; COM_OUT - Transmit character to serial port
;
; Call with: AL = character
; Returns: Nothing
; Destroys: Nothing
;
com_out proc near
push dx ; save register DX
push ax ; save character to send
mov dx,com_sts ; DX = status port address
com_out1: ; check if transmit buffer
in al,dx ; is empty (TBE bit = set)
and al,20h
jz com_out1 ; no, must wait
pop ax ; retrieve character
mov dx,com_data ; DX = data port address
out dx,al ; transmit the character
pop dx ; restore register DX
ret ; and return to caller
com_out endp
;
; PC_STAT - Get keyboard status
;
; Call with: Nothing
; Returns: Zero flag = false if character ready
; Zero flag = true if no character waiting
; Destroys: DX
;
pc_stat proc near
mov al,in_flag ; if character already
or al,al ; waiting, return status
jnz pc_stat1 ; in Zero flag
mov ah,6 ; otherwise call DOS to
mov dl,0ffh ; determine keyboard status
int 21h
jz pc_stat1 ; jump if no key ready
mov in_char,al ; got key, save it for
mov in_flag,0ffh ; "pc_in" routine
pc_stat1: ; return to caller with
ret ; status in Zero flag
pc_stat endp
;
; PC_IN - Get character from keyboard
;
; Call with: Nothing
; Returns: AL = character
; Destroys: DX
;
pc_in proc near
mov al,in_flag ; key already waiting?
or al,al
jnz pc_in1 ; yes,return it to caller
call pc_stat ; no, try and read char.
jmp pc_in
pc_in1: mov in_flag,0 ; clear char. waiting flag
mov al,in_char ; and return AL = char.
ret
pc_in endp
;
; PC_OUT - Send character to PC screen
;
; Call with: AL = character
; Returns: Nothing
; Destroys: AX, DX
;
pc_out proc near
mov dl,al ; DL = char to output
mov ah,6 ; function 6 = direct I/O
int 21h ; transfer to MS-DOS
ret ; return to caller
pc_out endp
;
; ASC_ENB - Enable comm port, capture communications interrupt
;
; Call with: Nothing
; Returns: Nothing
; Destroys: AX, BX, DX, ES
;
asc_enb proc near
mov ax,3500h+com_int ; save address of previous
int 21h ; owner of comm interrupt
mov word ptr oldcomm+2,es ; function 35H returns
mov word ptr oldcomm,bx ; vector in ES:BX
; install our comm handler
push ds ; save our data segment
mov ax,cs ; DS:DX = address of
mov ds,ax ; comm interrupt handler
mov dx,offset asc_isr
mov ax,2500h+com_int ; Fxn 25H = set vector
int 21h ; transfer to MS-DOS
pop ds ; restore data segment
mov dx,com_mcr ; set modem control reg.
mov al,0bh ; DTR and OUT2 bits
out dx,al ; to enable interrupts
mov dx,com_ier ; set interrupt enable
mov al,1 ; register on serial
out dx,al ; port controller
in al,pic_mask ; read 8259 interrupt mask
and al,not int_mask ; set mask for COM port
out pic_mask,al ; write new 8259 mask
ret ; back to caller
asc_enb endp
;
; ASC_DSB - Release communications interrupt, disable comm hardware
;
; Call with: Nothing
; Returns: Nothing
; Destroys: AX, DX
;
asc_dsb proc near
in al,pic_mask ; read 8259 interrupt mask
or al,int_mask ; reset mask for COM port
out pic_mask,al ; write new 8259 mask
push ds ; save our data segment
lds dx,oldcomm ; DX:DX = address of prev
; comm interrupt handler
mov ax,2500h+com_int ; Fxn 25H = set vector
int 21h ; transfer to MS-DOS
pop ds ; restore data segment
ret ; back to caller
asc_dsb endp
;
; ASC_ISR - Communications interrupt service routine
; Call with: Nothing
; Returns: Nothing
; Destroys: Nothing
;
asc_isr proc far
sti ; turn interrupts back on
push ax ; save registers
push bx
push dx
push ds
mov ax,_DATA ; make our data segment
mov ds,ax ; addressable
mov dx,com_data ; DX = data port address
in al,dx ; read this character
mov bx,asc_in ; get buffer pointer
mov [asc_buf+bx],al ; store this character
inc bx ; bump pointer
cmp bx,bufsiz ; time for wrap?
jne asc_isr1 ; no, jump
xor bx,bx ; yes, reset pointer
asc_isr1: ; store updated pointer
mov asc_in,bx
mov al,20h ; send EOI to 8259
out pic_eoi,al
pop ds ; restore all registers
pop dx
pop bx
pop ax
iret ; return from interrupt
asc_isr endp
_TEXT ends
_STK segment para stack 'STACK'
db 128 dup (?)
_STK ends
end talk